﻿using System;
using System.ComponentModel.Composition;
using System.Globalization;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;

namespace Lhn.Ds.Core.CarDevice
{
    [Export]
    public class UsbCardDevice
    {
        //常量定义
        public const byte BLOCK0_EN = 0x01; //操作第0块
        public const byte BLOCK1_EN = 0x02; //操作第1块
        public const byte BLOCK2_EN = 0x04; //操作第2块
        public const byte NEEDSERIAL = 0x08; //仅对指定序列号的卡操作
        public const byte EXTERNKEY = 0x10; //每次需要指定密码
        public const byte NEEDHALT = 0x20; //读卡或写卡后顺便休眠该卡，休眠后，卡必须拿离开感应区，再放回感应区，才能进行第二次操作。

        /// <summary>
        ///     返回认证密码A
        /// </summary>
        public static byte AuthModeA => 0x01;

        /// <summary>
        ///     返回认证密码B
        /// </summary>
        public static byte AuthModeB => 0x00;


        /// <summary>
        ///     外部函数声明：让设备发出声响 pcdbeep
        /// </summary>
        /// <param name="xms">xms单位为毫秒 </param>
        /// <returns></returns>
        [DllImport("dll/OUR_MIFARE.dll", EntryPoint = "pcdbeep")]
        public static extern byte pcdbeep(uint xms); //xms单位为毫秒 

        /// <summary>
        ///     外部函数声明：让设备发出声响 picchalt
        /// </summary>
        /// <param name="xms">睡眠卡 </param>
        /// <returns></returns>
        [DllImport("dll/OUR_MIFARE.dll", EntryPoint = "picchalt")]
        public static extern byte picchalt(); // 


        /// <summary>
        ///     读取设备编号，可做为软件加密狗用,也可以根据此编号在公司网站上查询保修期限 pcdgetdevicenumber
        /// </summary>
        /// <param name="devicenumber"></param>
        /// <returns></returns>
        [DllImport("dll/OUR_MIFARE.dll", EntryPoint = "pcdgetdevicenumber")]
        public static extern byte pcdgetdevicenumber(byte[] devicenumber); //devicenumber用于返回编号 


        /// <summary>
        ///     piccreadex 轻松读卡
        /// </summary>
        /// <param name="ctrlword">ctrlword：控制字</param>
        /// <param name="serial">serial：卡序列号数组，用于指定或返回卡序列号</param>
        /// <param name="area">area：指定读卡区号</param>
        /// <param name="keyA1B0">keyA1B0：指定用A或B密码认证,一般是用A密码，只有特殊用途下才用B密码，在这不做详细解释。</param>
        /// <param name="picckey">picckey：指定卡密码，6个字节，卡出厂时的初始密码为6个0xff</param>
        /// <param name="piccdata0_2">piccdata0_2：用于返回卡该区第0块到第2块的数据，共48个字节.</param>
        /// <returns></returns>
        [DllImport("dll/OUR_MIFARE.dll", CharSet = CharSet.Ansi, ThrowOnUnmappableChar = false, BestFitMapping = false,
            SetLastError = false)]
        public static extern byte piccreadex(byte ctrlword, byte[] serial, byte area, byte keyA1B0, byte[] picckey,
            byte[] piccdata0_2);


        /// <summary>
        ///     轻松写卡 piccwriteex
        /// </summary>
        /// <param name="ctrlword">ctrlword：控制字</param>
        /// <param name="serial">serial：卡序列号数组，用于指定或返回卡序列号</param>
        /// <param name="area">area：指定读卡区号</param>
        /// <param name="keyA1B0">keyA1B0：指定用A或B密码认证,一般是用A密码，只有特殊用途下才用B密码，在这不做详细解释。</param>
        /// <param name="picckey">picckey：指定卡密码，6个字节，卡出厂时的初始密码为6个0xff</param>
        /// <param name="piccdata0_2">piccdata0_2：用于返回卡该区第0块到第2块的数据，共48个字节.</param>
        /// <returns></returns>
        [DllImport("dll/OUR_MIFARE.dll", EntryPoint = "piccwriteex")]
        public static extern byte piccwriteex(byte ctrlword, byte[] serial, byte area, byte keyA1B0, byte[] picckey,
            byte[] piccdata0_2);


        /// <summary>
        ///     piccchangesinglekey 修改卡单区的密码
        /// </summary>
        /// <param name="ctrlword">ctrlword：控制字</param>
        /// <param name="serial">serial：卡序列号数组，用于指定或返回卡序列号</param>
        /// <param name="area">area：指定读卡区号</param>
        /// <param name="keyA1B0">keyA1B0：指定用A或B密码认证,一般是用A密码，只有特殊用途下才用B密码，在这不做详细解释。</param>
        /// <param name="piccoldkey">piccoldkey：//旧密码</param>
        /// <param name="piccnewkey">piccnewkey：//新密码.</param>
        /// <returns></returns>
        [DllImport("dll/OUR_MIFARE.dll", EntryPoint = "piccchangesinglekey")]
        public static extern byte piccchangesinglekey(byte ctrlword, byte[] serial, byte area, byte keyA1B0,
            byte[] piccoldkey, byte[] piccnewkey);
        /// <summary>
        /// 寻卡并返回该卡的序列号
        /// unsigned char __stdcall piccrequest(unsigned char *serial)
        /// </summary>
        /// <param name="serial">
        /// 形参*serial只需指向一个至少已分配了4个char空间的可写数组 unsigned char *指针， serial的下标由0开始。Serial数组的内容无需赋值，因为此数组仅用于返回值
        /// </param>
        /// <returns></returns>
        [DllImport("dll/OUR_MIFARE.dll", EntryPoint = "piccrequest")]
        public static extern byte piccrequest(byte[] serial);

        /// <summary>
        /// 寻卡并选中指定序列号的IC卡，必须指定序列号
        /// unsigned char __stdcall piccrequestex (unsigned char *serial)
        /// </summary>
        /// <param name="serial">
        /// 形参*serial只需指向一个至少已分配了4个char空间的数组 unsigned char *指针， serial的下标由0开始, serial数组的值为需要寻卡选卡的卡序列号。
        /// </param>
        /// <returns></returns>
        [DllImport("dll/OUR_MIFARE.dll", EntryPoint = "piccrequestex")]
        public static extern byte piccrequestex(byte[] serial);

        public static byte GetCtrlWordByte(bool nBLOCK0, bool nBLOCK1, bool nBLOCK2, bool nNEEDSERIAL, bool nNEEDHALT)
        {
            byte ctrlword = 0x00;
            if (nBLOCK0)
                ctrlword = BLOCK0_EN;
            if (nBLOCK1)
                ctrlword += BLOCK1_EN;
            if (nBLOCK2)
                ctrlword += BLOCK2_EN;
            if (nNEEDSERIAL)
                ctrlword += NEEDSERIAL;
            ctrlword += EXTERNKEY;
            if (nNEEDHALT)
                ctrlword += NEEDHALT;
            return ctrlword;
        }

        public static byte GetCtrlWordByte(bool nBLOCK0, bool nBLOCK1, bool nBLOCK2, bool nNEEDSERIAL)
        {
            byte ctrlword = 0x00;
            if (nBLOCK0)
                ctrlword = BLOCK0_EN;
            if (nBLOCK1)
                ctrlword += BLOCK1_EN;
            if (nBLOCK2)
                ctrlword += BLOCK2_EN;
            if (nNEEDSERIAL)
                ctrlword += NEEDSERIAL;
            ctrlword += EXTERNKEY;
            return ctrlword;
        }


        public static string GetDefineCardMessage(byte status)
        {
            switch (status)
            {
                case 0:
                    return "操作成功!";
                case 8:
                    return "寻卡错误";
                case 9:
                    return "读序列号错误!";
                case 10:
                    return "该卡可能已被休眠，无法选中，但卡序列号已被读出!";
                case 11:
                    return "装载密码错误!";
                case 12:
                    return "密码认证错误!";
                case 13:
                    return "读卡错误!";
                case 14:
                    return "写卡错误!";
                case 21:
                    return "动态库ICUSB.DLL不在当前目录下!";
                case 22:
                    return "动态库或驱动程序异常!动态库或驱动程序异常，解决方法是退出程序，拔出IC卡读写器，重装驱动程序再插上IC卡读写器重试，或者重新拷贝动态库OUR_MIFARE.dll到正确的位置。";
                case 23:
                    return "驱动程序错误或尚未安装!";
                case 24:
                    return "操作超时。可能是电脑中毒导致USB帧传递调度缓慢，或者是IC卡读写器有问题，解决方法是重启电脑或重新拔插IC卡读写器。";
                case 25:
                    return "发送字数不够!";
                case 26:
                    return "发送的CRC错误!";
                case 27:
                    return "USB传输不稳定!";
                case 28:
                    return "USB传输不稳定导致CRC校验错。";
                default:
                    return "出现未知错误!";
            }
        }

        //卡状态名称转成值
        public static string GetCardValue(string name)
        {
            switch (name)
            {
                case "ucsNormal":
                    return "1";
                case "ucsChanging":
                    return "2";
                case "ucsLosted":
                    return "3";
                default:
                    return "0";
            }
        }


        #region 基本函数

        public static byte[] GetHexNumberByte(long number, int len)
        {
            len = len * 2;
            var numberstr = number.ToString("X").PadLeft(len, '0');
            var bytebuff = new byte[len / 2];

            for (var i = 0; i < len; i += 2)
                bytebuff[i / 2] = byte.Parse(numberstr.Substring(i, 2), NumberStyles.HexNumber);

            return bytebuff;
        }

        //反转
        public static byte[] GetHexNumberByteA(long number, int len)
        {
            len = len * 2;
            var numberstr = number.ToString("X").PadLeft(len, '0');
            var bytebuff = new byte[len / 2];

            for (var i = 0; i < len; i += 2)
                bytebuff[i / 2] = byte.Parse(numberstr.Substring(i, 2), NumberStyles.HexNumber);

            Array.Reverse(bytebuff);
            return bytebuff;
        }

        public static byte[] GetHexTimeByte(long number, int len)
        {
            len = len * 2;
            var numberstr = number.ToString().PadLeft(len, '0');
            var bytebuff = new byte[len / 2];
            for (var i = 0; i < len; i += 2)
            {
                var hexnum = Convert.ToInt32(numberstr.Substring(i, 2)).ToString("X");
                bytebuff[i / 2] = byte.Parse(hexnum, NumberStyles.HexNumber);
            }

            return bytebuff;
        }

        public static byte[] GetHexStringByte(string hexNumber, int len)
        {
            len = len * 2;
            var bytebuff = new byte[len / 2];
            for (var i = 0; i < len; i += 2)
                bytebuff[i / 2] = byte.Parse(hexNumber.Substring(i, 2), NumberStyles.HexNumber);
            return bytebuff;
        }

        //public static byte[] GetHexNumberByte(int number, int len)
        public static byte[] GetHexNumberByte(int number, int len)
        {
            len = len * 2;
            var numberstr = number.ToString("X").PadLeft(len, '0');

            var bytebuff = new byte[len / 2];
            for (var i = 0; i < len; i += 2)
                bytebuff[i / 2] = byte.Parse(numberstr.Substring(i, 2), NumberStyles.HexNumber);
            return bytebuff;
        }

        public static byte GetCRC8(byte[] ptr, int len)
        {
            byte crc = 0;
            int i;
            for (var j = 0; j < len; j++)
            {
                crc ^= ptr[j];
                for (i = 0; i < 8; i++)
                    if ((crc & 0x01) == 1)
                        crc = (byte) ((crc >> 1) ^ 0x8C);
                    else
                        crc >>= 1;
            }

            return crc;
        }

        public static byte GetCRC8(byte[] buffer)
        {
            byte crc = 0;
            for (var j = 0; j < buffer.Length; j++)
            {
                crc ^= buffer[j];
                for (var i = 0; i < 8; i++)
                    if ((crc & 0x01) != 0)
                    {
                        crc >>= 1;
                        crc ^= 0x8c;
                    }
                    else
                    {
                        crc >>= 1;
                    }
            }

            return crc;
        }

        public static string GetHexNumberDateTime(byte[] byteBuff, int startIndex, int endIndex)
        {
            var len = endIndex - startIndex + 1;

            var newByteBuff = new byte[len];
            var ms = new MemoryStream(byteBuff);
            ms.Position = startIndex;
            ms.Read(newByteBuff, 0, len);
            var Hex = "";
            for (var i = 0; i < len; i++)
                Hex += int.Parse(newByteBuff[i].ToString("X").PadLeft(2, '0'), NumberStyles.HexNumber).ToString()
                    .PadLeft(2, '0');
            return "20" + Hex;
        }

        public static string GetHex16String(byte[] byteBuff, int startIndex, int endIndex)
        {
            var len = endIndex - startIndex + 1;

            var newByteBuff = new byte[len];
            var ms = new MemoryStream(byteBuff);
            ms.Position = startIndex;
            ms.Read(newByteBuff, 0, len);
            var Hex = "";
            for (var i = 0; i < len; i++) Hex += newByteBuff[i].ToString("X").PadLeft(2, '0');
            return Hex;
        }

        public static decimal GetHexDecimal(byte[] byteBuff, int startIndex, int endIndex)
        {
            var len = endIndex - startIndex + 1;

            var newByteBuff = new byte[len];
            var ms = new MemoryStream(byteBuff);
            ms.Position = startIndex;
            ms.Read(newByteBuff, 0, len);
            var Hex = "";
            for (var i = 0; i < len; i++) Hex += newByteBuff[i].ToString("X").PadLeft(2, '0');
            return decimal.Parse(Convert.ToInt32(Hex, 16).ToString()) / 100.0M;
        }

        public static decimal GetHexDecimalA(byte[] byteBuff, int startIndex, int endIndex)
        {
            var len = endIndex - startIndex + 1;

            var newByteBuff = new byte[len];
            var ms = new MemoryStream(byteBuff);
            ms.Position = startIndex;
            ms.Read(newByteBuff, 0, len);

            Array.Reverse(newByteBuff);

            var Hex = "";
            for (var i = 0; i < len; i++) Hex += newByteBuff[i].ToString("X").PadLeft(2, '0');
            return decimal.Parse(Convert.ToInt32(Hex, 16).ToString()) / 100.0M;
        }

        public static int GetHexNumberInt32(byte[] byteBuff, int startIndex, int endIndex)
        {
            var len = endIndex - startIndex + 1;

            var newByteBuff = new byte[len];
            var ms = new MemoryStream(byteBuff);
            ms.Position = startIndex;
            ms.Read(newByteBuff, 0, len);
            var Hex = "0";
            if (len > 0) Hex = "";
            for (var i = 0; i < len; i++) Hex += newByteBuff[i].ToString("X").PadLeft(2, '0');
            var number = int.Parse(Hex, NumberStyles.HexNumber);
            return number;
            ;
        }

        //反转
        public static int GetHexNumberInt32A(byte[] byteBuff, int startIndex, int endIndex)
        {
            var len = endIndex - startIndex + 1;

            var newByteBuff = new byte[len];
            var ms = new MemoryStream(byteBuff);
            ms.Position = startIndex;
            ms.Read(newByteBuff, 0, len);

            Array.Reverse(newByteBuff);

            var Hex = "0";
            if (len > 0) Hex = "";
            for (var i = 0; i < len; i++) Hex += newByteBuff[i].ToString("X").PadLeft(2, '0');
            var number = int.Parse(Hex, NumberStyles.HexNumber);
            return number;
            ;
        }

        public static long GetHexNumberDec(byte[] byteBuff, int startIndex, int endIndex)
        {
            var len = endIndex - startIndex + 1;

            var newByteBuff = new byte[len];
            var ms = new MemoryStream(byteBuff);
            ms.Position = startIndex;
            ms.Read(newByteBuff, 0, len);
            var Hex = "0";
            if (len > 0) Hex = "";
            for (var i = 0; i < len; i++) Hex += newByteBuff[i].ToString("X").PadLeft(2, '0');
            var number =
                Convert.ToInt64(Convert.ToInt64(Hex, 16).ToString()); //decimal.Parse(Hex, NumberStyles.HexNumber);

            return number;
            ;
        }

        //反转
        public static long GetHexNumberDecA(byte[] byteBuff, int startIndex, int endIndex)
        {
            var len = endIndex - startIndex + 1;

            var newByteBuff = new byte[len];
            var ms = new MemoryStream(byteBuff);
            ms.Position = startIndex;
            ms.Read(newByteBuff, 0, len);

            Array.Reverse(newByteBuff);

            var Hex = "0";
            if (len > 0) Hex = "";
            //
            for (var i = 0; i < len; i++) Hex += newByteBuff[i].ToString("X").PadLeft(2, '0');
            var number =
                Convert.ToInt64(Convert.ToInt64(Hex, 16).ToString()); //decimal.Parse(Hex, NumberStyles.HexNumber);

            return number;
            ;
        }

        #endregion
    }
}